<!DOCTYPE html><html class="appearance-auto" lang="zh-CN"><head><meta charset="UTF-8"><title>【Spring源码解析系列】 lookup-method和replaced-method</title><meta name="description" content="用创业者心态做好每一件事情！"><meta name="viewport" content="width=device-width, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no, initial-scale=1"><!-- Google Analytics --><!-- End Google Analytics -->
<!-- Baidu Analytics --><!-- End Baidu Analytics --><link rel="icon" href="/blog/images/favicon.png"><link rel="stylesheet" href="/blog/style/common/bulma.css"><link rel="stylesheet" href="/blog/style/base.css"><link rel="stylesheet" href="/blog/style/common/helper.css"><script src="/blog/js/common.js"></script><link rel="stylesheet" href="/blog/style/post.css"><link rel="stylesheet" href="/blog/style/themes/highlight-theme-light.css"><script src="/blog/js/highlight.pack.js"></script><meta name="description" content="
本文主要讲述Spring中lookup-method和replaced-method


作用lookup-method ：用于注入方法返回结果，也就是说能通过配置方式替换方法返回结果。(在方法或者抽象方法上使用@Lookup注解，将会根据该方法的返回值，自动在BeanFactory中调用getBean()来注入该Bean)replaced-method ：可以实现方法主体或返回结果的替换，通俗来讲 ： lookup-method 可以注入属性bean， replaced-method 替换方法实现
示例// 基类接口
public interface DemoBase &amp;#123;
    String hello();
&amp;#125;

...
public class DemoA implements.."><!-- hexo injector head_end start -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css">

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/hexo-math@4.0.0/dist/style.css">
<!-- hexo injector head_end end --><meta name="generator" content="Hexo 5.4.1"><link rel="alternate" href="/blog/atom.xml" title="janedler's blog" type="application/atom+xml">
</head><body class="is-flex is-flex-direction-column"><header class="header-widget is-flex-shrink-0 is-hidden-mobile"><div class="container is-fullhd is-flex is-justify-content-space-between is-align-items-center is-full-height"><section class="is-hidden-mobile is-flex-shrink-0"><h2><a href="/blog/">田园牧歌(*︶*)'s blog</a></h2></section><h3 class="is-hidden-mobile is-family-serif is-full-height is-flex is-align-items-center is-flex-shrink-0"><div class="is-full-height" id="postTopic"><p class="is-full-height is-flex-shrink-0 is-flex is-align-items-center is-justify-content-center">【Spring源码解析系列】 lookup-method和replaced-method</p><p class="is-full-height is-flex-shrink-0 is-flex is-align-items-center is-justify-content-center">点击返回顶部</p></div></h3><aside class="is-flex-shrink-0"><h3 class="is-inline-block"><a href="/blog/">首页</a></h3><h3 class="is-inline-block"><a href="/blog/about">关于</a></h3><h3 class="is-inline-block"><a href="/blog/archives">归档</a></h3></aside></div></header><header class="is-flex header-widget is-flex-shrink-0 is-align-items-center is-justify-content-center is-hidden-tablet"><h3 class="is-inline-block"><a href="/blog/">首页</a></h3><h3 class="is-inline-block"><a href="/blog/about">关于</a></h3><h3 class="is-inline-block"><a href="/blog/archives">归档</a></h3></header><main><main class="container is-max-widescreen content section post-page pt-4 px-4"><div class="columns is-flex-desktop is-justify-content-center is-flex-direction-row-reverse"><div class="column is-3 is-hidden-mobile"><ol class="toc"><li class="toc-item toc-level-2"><a class="toc-link" href="#%E4%BD%9C%E7%94%A8"><span class="toc-text">作用</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E7%A4%BA%E4%BE%8B"><span class="toc-text">示例</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90"><span class="toc-text">源码解析</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#LookupOverrideMethodInterceptor"><span class="toc-text">LookupOverrideMethodInterceptor</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#ReplaceOverrideMethodInterceptor"><span class="toc-text">ReplaceOverrideMethodInterceptor</span></a></li></ol></li></ol></li></ol></div><div class="column is-9"><header class="my-4"><a href="/blog/tags/Java"><i class="tag post-item-tag">Java</i></a><a href="/blog/tags/Spring"><i class="tag post-item-tag">Spring</i></a></header><h1 class="mt-0 mb-1 is-family-serif" id="postTitle">【Spring源码解析系列】 lookup-method和replaced-method</h1><time class="has-text-grey" datetime="2022-11-30T16:00:00.000Z">2022-12-01</time><article class="mt-2 post-content"><p><img src="https://janedler.oss-cn-beijing.aliyuncs.com/images/spring.png" alt="cover"></p>
<p>本文主要讲述Spring中lookup-method和replaced-method</p>
<span id="more"></span>

<h2 id="作用"><a href="#作用" class="headerlink" title="作用"></a>作用</h2><p>lookup-method ：用于注入方法返回结果，也就是说能通过配置方式替换方法返回结果。(在方法或者抽象方法上使用@Lookup注解，将会根据该方法的返回值，自动在BeanFactory中调用getBean()来注入该Bean)<br>replaced-method ：可以实现方法主体或返回结果的替换，通俗来讲 ： lookup-method 可以注入属性bean， replaced-method 替换方法实现</p>
<h2 id="示例"><a href="#示例" class="headerlink" title="示例"></a>示例</h2><pre><code class="java">// 基类接口
public interface DemoBase &#123;
    String hello();
&#125;

...
public class DemoA implements DemoBase &#123;
    public DemoBase getDemoBase() &#123;
        return new DemoB();
    &#125;

    @Override
    public String hello() &#123;
        return &quot;demoA hello&quot;;
    &#125;
&#125;
...
public class DemoB implements DemoBase &#123;
    @Override
    public String hello() &#123;
        return &quot;DemoB hello&quot;;
    &#125;
&#125;
...
public class DemoC implements DemoBase&#123;
    @Override
    public String hello() &#123;
        return &quot;DemoC hello&quot;;
    &#125;
&#125;
....

public class DemoMethodReplacer implements MethodReplacer &#123;
    @Override
    public Object reimplement(Object obj, Method method, Object[] args) throws Throwable &#123;
        System.out.println(&quot;method = &quot; + method);
        return &quot;reimplement&quot;;
    &#125;
&#125;
</code></pre>
<pre><code class="java">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;beans xmlns=&quot;http://www.springframework.org/schema/beans&quot;
       xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
       xsi:schemaLocation=&quot;http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd&quot;&gt;
    &lt;bean id=&quot;demoA&quot;  name=&quot;demoA&quot;  class=&quot;com.kingfish.springbootdemo.property.DemoA&quot;&gt;
        // 指定 DemoA.getDemoBase 方法，返回值是 容器中的demoC
        &lt;lookup-method name=&quot;getDemoBase&quot; bean=&quot;demoC&quot;&gt;&lt;/lookup-method&gt;
        // 指定 DemoA.hello方法，其方法经过 demoMethodReplacer.reimplement 代理
        &lt;replaced-method name=&quot;hello&quot; replacer=&quot;demoMethodReplacer&quot;&gt;&lt;/replaced-method&gt;
    &lt;/bean&gt;
    &lt;bean id=&quot;demoB&quot; name=&quot;demoB&quot; class=&quot;com.kingfish.springbootdemo.property.DemoB&quot;&gt;&lt;/bean&gt;
    &lt;bean id=&quot;demoC&quot; name=&quot;demoC&quot; class=&quot;com.kingfish.springbootdemo.property.DemoC&quot;&gt;&lt;/bean&gt;
    &lt;bean id=&quot;demoMethodReplacer&quot; name=&quot;demoMethodReplacer&quot; class=&quot;com.kingfish.springbootdemo.property.DemoMethodReplacer&quot;&gt;&lt;/bean&gt;
&lt;/beans&gt;
</code></pre>
<pre><code class="java">@SpringBootApplication
@ImportResource(&quot;bean.xml&quot;)
public class SpringbootDemoApplication implements ApplicationRunner &#123;

    public static void main(String[] args) &#123;
        ConfigurableApplicationContext run = SpringApplication.run(SpringbootDemoApplication.class, args);
        DemoA demoA = run.getBean(DemoA.class);
        System.out.println(demoA.hello());
        System.out.println(demoA.getDemoBase());

&#125;
</code></pre>
<p><img src="https://janedler.oss-cn-beijing.aliyuncs.com/images/202212011620671.png" alt="在这里插入图片描述"></p>
<p>lookup-method : name 指定方法名，bean 指定方法返回的bean。这里注意上面的demo中getDemoBase方法没有必要实现，可以直接写一个抽象方法。即当调用 demoA.getDemoBase 方法时返回的是 Spring 容器中name为demoC 的bean。这里可以通过将demoC 设置成原型模式来实现单例模式下原型bean的获取。<br>replaced-mthod : name 指定方法名，replacer 指定代理类。即当调用demoA中的hello方法时，会经过demoMethodReplacer的代理。demoMethodReplacer需要实现MethodReplacer 接口，调用方法时会经过MethodReplacer.reimplement 方法。</p>
<h2 id="源码解析"><a href="#源码解析" class="headerlink" title="源码解析"></a>源码解析</h2><p>当前bean使用了 lookup-method，replaced-method 属性，则RootBeanDefinition::hasMethodOverrides则为true，实例化bean的策略就采用CGLib。</p>
<pre><code class="java">// 没有覆盖，直接使用反射实例化即可
if (!bd.hasMethodOverrides()) &#123;
   // 通过 BeanUtils 直接使用构造器对象实例化 Bean 对象 JDK
 return BeanUtils.instantiateClass(ctor, args);
&#125;
else &#123;
   // 生成 CGLIB 创建的子类对象
   return instantiateWithMethodInjection(bd, beanName, owner, ctor, args);
&#125;
</code></pre>
<p>CglibSubclassingInstantiationStrategy::instantiate</p>
<pre><code class="java">//使用CGLIB进行Bean对象的实例化
public Object instantiate(@Nullable Constructor&lt;?&gt; ctor, Object... args) &#123;
   //通过 Cglib 创建一个代理类
   Class&lt;?&gt; subclass = createEnhancedSubclass(this.beanDefinition);
   ...代码省略...
   Factory factory = (Factory) instance;
   factory.setCallbacks(new Callback[] &#123;NoOp.INSTANCE,
         new LookupOverrideMethodInterceptor(this.beanDefinition, this.owner),
         new ReplaceOverrideMethodInterceptor(this.beanDefinition, this.owner)&#125;);
   return instance;
&#125;
</code></pre>
<pre><code class="java">public Class&lt;?&gt; createEnhancedSubclass(RootBeanDefinition beanDefinition) &#123;
   //CGLIB里面的类
   Enhancer enhancer = new Enhancer();
   //将Bean本身作为其基类
   enhancer.setSuperclass(beanDefinition.getBeanClass());
   enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
   enhancer.setAttemptLoad(true);
   if (this.owner instanceof ConfigurableBeanFactory) &#123;
      ClassLoader cl = ((ConfigurableBeanFactory) this.owner).getBeanClassLoader();
      enhancer.setStrategy(new ClassLoaderAwareGeneratorStrategy(cl));
   &#125;
   enhancer.setCallbackFilter(new MethodOverrideCallbackFilter(beanDefinition));
   enhancer.setCallbackTypes(CALLBACK_TYPES);
   //使用CGLIB的create方法生成实例对象
   return enhancer.createClass();
&#125;
</code></pre>
<p>通过 MethodOverrideCallbackFilter 来定义调用 callback 类型。<br>MethodOverrideCallbackFilter 是用来定义 CGLIB 回调过滤方法的拦截器行为，它继承 CglibIdentitySupport 实现 CallbackFilter 接口。CallbackFilter 是 CGLIB 的一个回调过滤器。<br>CglibIdentitySupport 则为 CGLIB 提供 #hashCode() 和 #equals(Object o) 方法，以确保 CGLIB 不会为每个 Bean 生成不同的类。</p>
<p>CallbackFilter::accept</p>
<pre><code class="java">@Override
public int accept(Method method) &#123;
   MethodOverride methodOverride = getBeanDefinition().getMethodOverrides().getOverride(method);
   if (logger.isTraceEnabled()) &#123;
      logger.trace(&quot;MethodOverride for &quot; + method + &quot;: &quot; + methodOverride);
   &#125;
   if (methodOverride == null) &#123;
      return PASSTHROUGH;
   &#125;
   else if (methodOverride instanceof LookupOverride) &#123;
      return LOOKUP_OVERRIDE;
   &#125;
   else if (methodOverride instanceof ReplaceOverride) &#123;
      return METHOD_REPLACER;
   &#125;
   throw new UnsupportedOperationException(&quot;Unexpected MethodOverride subclass: &quot; +
         methodOverride.getClass().getName());
&#125;
</code></pre>
<pre><code class="java">private static final Class&lt;?&gt;[] CALLBACK_TYPES = new Class&lt;?&gt;[]
      &#123;NoOp.class, LookupOverrideMethodInterceptor.class, ReplaceOverrideMethodInterceptor.class&#125;;
</code></pre>
<p>定义了两个熟悉的拦截器 ：LookupOverrideMethodInterceptor 和 ReplaceOverrideMethodInterceptor，两个拦截器分别对应两个不同的 callback 业务</p>
<h4 id="LookupOverrideMethodInterceptor"><a href="#LookupOverrideMethodInterceptor" class="headerlink" title="LookupOverrideMethodInterceptor"></a>LookupOverrideMethodInterceptor</h4><pre><code class="java">private static class LookupOverrideMethodInterceptor extends CglibIdentitySupport implements MethodInterceptor &#123;

      private final BeanFactory owner;

      public LookupOverrideMethodInterceptor(RootBeanDefinition beanDefinition, BeanFactory owner) &#123;
         super(beanDefinition);
         this.owner = owner;
      &#125;

      @Override
      public Object intercept(Object obj, Method method, Object[] args, MethodProxy mp) throws Throwable &#123;
         // Cast is safe, as CallbackFilter filters are used selectively.
         // 获得 method 对应的 LookupOverride 对象
         LookupOverride lo = (LookupOverride) getBeanDefinition().getMethodOverrides().getOverride(method);
         Assert.state(lo != null, &quot;LookupOverride not found&quot;);
         // 获得参数
         Object[] argsToUse = (args.length &gt; 0 ? args : null);  // if no-arg, don&#39;t insist on args at all
         // 获得 Bean
//       if (StringUtils.hasText(lo.getBeanName())) &#123; // Bean 的名字
            Object bean = (argsToUse != null ? this.owner.getBean(lo.getBeanName(), argsToUse) :
                  this.owner.getBean(lo.getBeanName()));
            // Detect package-protected NullBean instance through equals(null) check
            return (bean.equals(null) ? null : bean);
         &#125;
         else &#123;// Bean 的类型
            // Find target bean matching the (potentially generic) method return type
            ResolvableType genericReturnType = ResolvableType.forMethodReturnType(method);
            return (argsToUse != null ? this.owner.getBeanProvider(genericReturnType).getObject(argsToUse) :
                  this.owner.getBeanProvider(genericReturnType).getObject());
         &#125;
      &#125;
   &#125;
</code></pre>
<h4 id="ReplaceOverrideMethodInterceptor"><a href="#ReplaceOverrideMethodInterceptor" class="headerlink" title="ReplaceOverrideMethodInterceptor"></a>ReplaceOverrideMethodInterceptor</h4><pre><code class="java">private static class ReplaceOverrideMethodInterceptor extends CglibIdentitySupport implements MethodInterceptor &#123;

   private final BeanFactory owner;

   public ReplaceOverrideMethodInterceptor(RootBeanDefinition beanDefinition, BeanFactory owner) &#123;
      super(beanDefinition);
      this.owner = owner;
   &#125;

   @Override
   public Object intercept(Object obj, Method method, Object[] args, MethodProxy mp) throws Throwable &#123;
      // 获得 method 对应的 LookupOverride 对象
      ReplaceOverride ro = (ReplaceOverride) getBeanDefinition().getMethodOverrides().getOverride(method);
      Assert.state(ro != null, &quot;ReplaceOverride not found&quot;);
      // TODO could cache if a singleton for minor performance optimization
      // 获得 MethodReplacer 对象
      MethodReplacer mr = this.owner.getBean(ro.getMethodReplacerBeanName(), MethodReplacer.class);
      // 执行替换
      return mr.reimplement(obj, method, args);
   &#125;
&#125;
</code></pre>
</article><section class="jump-container is-flex is-justify-content-space-between my-6"><!-- em is empty placeholder--><a class="button is-default" href="/blog/2022/12/01/Spring/Spring-autowiring%E5%8E%9F%E7%90%86/" title="【Spring源码解析系列】 autowiring原理"><i class="iconfont icon-prev mr-2 has-text-grey"></i><span class="has-text-weight-semibold">上一页: 【Spring源码解析系列】 autowiring原理</span></a><a class="button is-default" href="/blog/2022/12/01/Spring/Spring-%E4%BA%8B%E5%8A%A1/" title="【Spring源码解析系列】 事务"><span class="has-text-weight-semibold">下一页: 【Spring源码解析系列】 事务</span><i class="iconfont icon-next ml-2 has-text-grey"></i></a></section><article class="mt-6 comment-container"><script async repo="janedler/blog" src="https://utteranc.es/client.js" issue-term="pathname" theme="preferred-color-scheme"></script></article></div></div></main></main><footer class="is-flex is-flex-direction-column is-align-items-center is-flex-shrink-0 is-family-serif"><section class="sns-container"><!-- Github--><a title="github" target="_blank" rel="noopener nofollow" href="//github.com//janedler"><i class="iconfont icon-github"></i></a><!-- Ins--><!-- RSS--><a title="rss" target="_blank" rel="noopener nofollow" href="/blog/atom.xml"><i class="iconfont icon-rss"></i></a><!-- 知乎--><!-- 领英--><!-- 脸书--></section><p><span>Copyright ©</span><span> 田园牧歌(*︶*) 2022</span></p><div class="is-flex is-justify-content-center is-flex-wrap-wrap"><p>Powered by Hexo &verbar;&nbsp;</p><p class="is-flex is-justify-content-center"><a title="Hexo theme author" target="_blank" rel="noopener" href="//github.com/haojen">Theme by Haojen&nbsp;</a></p><div style="margin-top: 2px"><a class="github-button" title="github-button" target="_blank" rel="noopener" href="https://github.com/haojen/hexo-theme-Claudia" data-color-scheme="no-preference: light; light: light; dark: dark;" data-show-count="true"></a></div></div><div><span></span></div></footer><script async defer src="https://buttons.github.io/buttons.js"></script><script src="/blog/js/post.js"></script></body></html>